/**************************************************************************************
 * Copyright (c) 2020 Institute of Computing Technology, CAS
 * Copyright (c) 2020 University of Chinese Academy of Sciences
 *
 * NutShell is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *             http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
 * FIT FOR A PARTICULAR PURPOSE.
 *
 * See the Mulan PSL v2 for more details.
 ***************************************************************************************/

package nutcore

import SSDbackend.StallPointConnect
import chisel3.{Flipped, Module, _}
import chisel3.util._
import com.sun.org.apache.xpath.internal.operations
import utils.PipelineConnect

//第一版先就将这部分都塞到一块
class Bypass extends Module{
  val io = IO(new Bundle {
    val in = Vec(2,Flipped(Decoupled(new DecodeIO)))
    val memStall = Input(Bool())
    val flush = Input(Vec(4,Bool()))
    val issueStall = Output(Vec(2,Bool()))
    val pipeFlush = Output(Vec(10,Bool()))
    val decodeBypassPkt = Output(Vec(2, Decoupled(new BypassPkt)))
    val BypassPkt = Output(Vec(10, Decoupled(new BypassPkt)))
  })
  val PipeLinePkt = Vec(10,Decoupled(new BypassPkt))
  //issue stall
  io.issueStall := DecodeIO2BypassPkt.io.issueStall
  //PktPipeline
  PktPipeline(new BypassPkt,DecodeIO2BypassPkt.io.out0,DecodeIO2BypassPkt.io.out1,PipeCtl.io.pipeCtl,PipeLinePkt)
  val PipeCtl = new PipeCtl
  val DecodeIO2BypassPkt = new DecodeIO2BypassPkt

  PipeCtl.io.i0pipeStall <> DecodeIO2BypassPkt.io.issueStall(1)
  PipeCtl.io.i1pipeStall <> DecodeIO2BypassPkt.io.issueStall(0)
  PipeCtl.io.flush <> io.flush
  PipeCtl.io.memStall <> io.memStall
  DecodeIO2BypassPkt.io.BypassPkt <> PipeLinePkt
  DecodeIO2BypassPkt.io.in <> io.in

  io.BypassPkt := PipeLinePkt
  io.decodeBypassPkt := Seq(DecodeIO2BypassPkt.io.out0,DecodeIO2BypassPkt.io.out1)
  io.pipeFlush := PipeCtl.io.pipeCtl.flush

}
class DecodeIO2BypassPkt extends Module {
  val io = IO(new Bundle {
    val in = Vec(2, Flipped(new DecodeIO))
    val BypassPkt = Vec(10, Flipped(Decoupled(new BypassPkt)))
    val issueStall = Output(Vec(2, Bool()))
    val out0 = new BypassPkt
    val out1 = new BypassPkt
  })
  //生成 BypassPkt， 以及issue stall 信号
  val i0decodePkt = new decodePkt
  val i1decodePkt = new decodePkt
  DecodeIO2decodePkt(io.in(0), i1decodePkt)
  DecodeIO2decodePkt(io.in(1), i0decodePkt)
  i0decodePkt <> io.out0
  i1decodePkt <> io.out1 //先简单连一下，之后再覆盖或补全其他输出信号

  //pipeline0 : 1,3,5,7,9
  //pipeline1 : 0,2,4,6,8
  //i0 i1 hit
  val i0rs1valid = false.B
  val i0rs2valid = false.B
  val i1rs1valid = false.B
  val i1rs2valid = false.B
  val i1hiti0 = false.B

  i0rs1valid := io.in(0).ctrl.src1Type === "b0".U
  i0rs2valid := io.in(0).ctrl.src2Type === "b0".U
  i1rs1valid := io.in(1).ctrl.src1Type === "b0".U
  i1rs2valid := io.in(1).ctrl.src2Type === "b0".U
  i1hiti0 := (io.in(1).ctrl.rfSrc1  === io.in(0).ctrl.rfDest && i1rs1valid && i0decodePkt.rdvalid
    || io.in(1).ctrl.rfSrc2  === io.in(0).ctrl.rfDest && i1rs2valid && i0decodePkt.rdvalid)
  //hit stage
  val i0rs1hitStage = 10.U(4.W)
  val i0rs2hitStage = 10.U(4.W)
  val i1rs1hitStage = 10.U(4.W)
  val i1rs2hitStage = 10.U(4.W)

  for (i <- 9 to 0) {
    when(io.in(0).ctrl.rfSrc1 === io.BypassPkt(i).bits.decodePkt.rd && io.BypassPkt(i).bits.decodePkt.rdvalid){
    i0rs1hitStage := i.asUInt()}
    when(io.in(0).ctrl.rfSrc2 === io.BypassPkt(i).bits.decodePkt.rd && io.BypassPkt(i).bits.decodePkt.rdvalid){
    i0rs2hitStage := i.asUInt()}
    when(io.in(0).ctrl.rfSrc1 === io.BypassPkt(i).bits.decodePkt.rd && io.BypassPkt(i).bits.decodePkt.rdvalid){
    i1rs1hitStage := i.asUInt()}
    when(io.in(0).ctrl.rfSrc2 === io.BypassPkt(i).bits.decodePkt.rd && io.BypassPkt(i).bits.decodePkt.rdvalid){
    i1rs2hitStage := i.asUInt()}
  }

  //merge decodePkt.subalu

  io.out0.decodePkt.subalu := (i0decodePkt.alu && i0rs1hitStage >= 0.U && i0rs1hitStage <= 3.U && (io.BypassPkt(i0rs1hitStage).bits.decodePkt.mul || io.BypassPkt(i0rs1hitStage).bits.decodePkt.load)
    || i0decodePkt.alu && i0rs2hitStage >= 0.U && i0rs2hitStage <= 3.U && (io.BypassPkt(i0rs2hitStage).bits.decodePkt.mul || io.BypassPkt(i0rs2hitStage).bits.decodePkt.load)
    || i0decodePkt.alu && i0rs1hitStage >= 0.U && i0rs1hitStage <= 5.U && io.BypassPkt(i0rs1hitStage).bits.decodePkt.subalu
    || i0decodePkt.alu && i0rs2hitStage >= 0.U && i0rs2hitStage <= 5.U && io.BypassPkt(i0rs2hitStage).bits.decodePkt.subalu
    )

  io.out1.decodePkt.subalu := (i1decodePkt.alu && i1rs1hitStage >= 1.U && i1rs1hitStage <= 3.U && (io.BypassPkt(i1rs1hitStage).bits.decodePkt.mul || io.BypassPkt(i1rs1hitStage).bits.decodePkt.load)
    || i1decodePkt.alu && i1rs2hitStage >= 1.U && i1rs2hitStage <= 3.U && (io.BypassPkt(i1rs2hitStage).bits.decodePkt.mul || io.BypassPkt(i1rs2hitStage).bits.decodePkt.load)
    || i1decodePkt.alu && i1rs1hitStage >= 1.U && i1rs1hitStage <= 5.U && io.BypassPkt(i1rs1hitStage).bits.decodePkt.subalu
    || i1decodePkt.alu && i1rs2hitStage >= 1.U && i1rs2hitStage <= 5.U && io.BypassPkt(i1rs2hitStage).bits.decodePkt.subalu
    || i1hiti0 && (i0decodePkt.mul || i0decodePkt.load) && i1decodePkt.alu
    )

  //issue stall
  //这部分暂时先只考虑整数指令需要stall的情况（额，整数指令好像这一步不会stall)
  io.issueStall := (false.B, false.B)
  //BypassCtl
  val FuType = Vec(10,new decodePkt)
  for(i <- 0 to 0) FuType(i) := io.BypassPkt(i).bits.decodePkt
  val Valid = Vec(10,Bool())
  for(i <- 0 to 0) Valid(i) := io.BypassPkt(i).valid
  //BypassPkt out0
  io.out0.BypassCtl.rs1bypasse0 := Seq(
    Valid(0) && i0rs1hitStage === 0.U && FuType(0).alu && !FuType(0).subalu,
    Valid(1) && i0rs1hitStage === 1.U && FuType(1).alu && !FuType(1).subalu,
    Valid(2) && i0rs1hitStage === 2.U && FuType(2).alu && !FuType(2).subalu,
    Valid(3) && i0rs1hitStage === 3.U && FuType(3).alu && !FuType(3).subalu,
    Valid(4) && i0rs1hitStage === 4.U && FuType(4).alu && !FuType(4).subalu,
    Valid(5) && i0rs1hitStage === 5.U && FuType(5).alu && !FuType(5).subalu,
    Valid(4) && i0rs1hitStage === 4.U && FuType(4).load || Valid(5) && i0rs1hitStage === 5.U && FuType(5).load,
    Valid(4) && i0rs1hitStage === 4.U && FuType(4).mul || Valid(5) && i0rs1hitStage === 5.U && FuType(5).mul,
    Valid(6) && i0rs1hitStage === 6.U ,
    Valid(7) && i0rs1hitStage === 7.U ,
    Valid(8) && i0rs1hitStage === 8.U ,
    Valid(9) && i0rs1hitStage === 9.U
    )
  io.out0.BypassCtl.rs2bypasse0 := Seq(
    Valid(0) && i0rs2hitStage === 0.U && FuType(0).alu && !FuType(0).subalu,
    Valid(1) && i0rs2hitStage === 1.U && FuType(1).alu && !FuType(1).subalu,
    Valid(2) && i0rs2hitStage === 2.U && FuType(2).alu && !FuType(2).subalu,
    Valid(3) && i0rs2hitStage === 3.U && FuType(3).alu && !FuType(3).subalu,
    Valid(4) && i0rs2hitStage === 4.U && FuType(4).alu && !FuType(4).subalu,
    Valid(5) && i0rs2hitStage === 5.U && FuType(5).alu && !FuType(5).subalu,
    Valid(4) && i0rs2hitStage === 4.U && FuType(4).load || Valid(5) && i0rs1hitStage === 5.U && FuType(5).load,
    Valid(4) && i0rs2hitStage === 4.U && FuType(4).mul || Valid(5) && i0rs1hitStage === 5.U && FuType(5).mul,
    Valid(6) && i0rs2hitStage === 6.U ,
    Valid(7) && i0rs2hitStage === 7.U ,
    Valid(8) && i0rs2hitStage === 8.U ,
    Valid(9) && i0rs2hitStage === 9.U
  )
  io.out0.BypassCtl.rs1bypasse2 := Seq(
    Valid(4) && i0rs1hitStage === 4.U && FuType(4).alu && FuType(4).subalu,
    Valid(5) && i0rs1hitStage === 5.U && FuType(5).alu && FuType(5).subalu
  )
  io.out0.BypassCtl.rs2bypasse2 := Seq(
    Valid(4) && i0rs2hitStage === 4.U && FuType(4).alu && FuType(4).subalu,
    Valid(5) && i0rs2hitStage === 5.U && FuType(5).alu && FuType(5).subalu
  )
  io.out0.BypassCtl.rs1bypasse3 := Seq(
    false.B,
    Valid(0) && i0rs1hitStage === 0.U && FuType(0).alu && FuType(0).subalu
      ||Valid(0) && i0rs1hitStage === 0.U && FuType(0).load
      ||Valid(0) && i0rs1hitStage === 0.U && FuType(0).mul,
    Valid(1) && i0rs1hitStage === 1.U && FuType(1).load
      || Valid(1) && i0rs1hitStage === 1.U && FuType(1).alu && FuType(1).subalu
      || Valid(1) && i0rs1hitStage === 1.U && FuType(1).mul,
    Valid(2) && i0rs1hitStage === 2.U && FuType(2).alu && FuType(2).subalu
      ||Valid(2) && i0rs1hitStage === 2.U && FuType(2).load
      ||Valid(2) && i0rs1hitStage === 2.U && FuType(2).mul,
    Valid(3) && i0rs1hitStage === 3.U && FuType(3).alu && FuType(3).subalu
     || Valid(3) && i0rs1hitStage === 3.U && FuType(3).load
     || Valid(3) && i0rs1hitStage === 3.U && FuType(3).mul
  )
  io.out0.BypassCtl.rs2bypasse3 := Seq(
    false.B,
    Valid(0) && i0rs2hitStage === 0.U && FuType(0).alu && FuType(0).subalu
      ||Valid(0) && i0rs2hitStage === 0.U && FuType(0).load
      ||Valid(0) && i0rs2hitStage === 0.U && FuType(0).mul,
    Valid(1) && i0rs2hitStage === 1.U && FuType(1).load
      || Valid(1) && i0rs2hitStage === 1.U && FuType(1).alu && FuType(1).subalu
      || Valid(1) && i0rs2hitStage === 1.U && FuType(1).mul,
    Valid(2) && i0rs2hitStage === 2.U && FuType(2).alu && FuType(2).subalu
      ||Valid(2) && i0rs2hitStage === 2.U && FuType(2).load
      ||Valid(2) && i0rs2hitStage === 2.U && FuType(2).mul,
    Valid(3) && i0rs2hitStage === 3.U && FuType(3).alu && FuType(3).subalu
      || Valid(3) && i0rs2hitStage === 3.U && FuType(3).load
      || Valid(3) && i0rs2hitStage === 3.U && FuType(3).mul
  )
  //BypassPkt out1
  io.out1.BypassCtl.rs1bypasse0 := Seq(
    Valid(0) && i1rs1hitStage === 0.U && FuType(0).alu && !FuType(0).subalu,
    Valid(1) && i1rs1hitStage === 1.U && FuType(1).alu && !FuType(1).subalu,
    Valid(2) && i1rs1hitStage === 2.U && FuType(2).alu && !FuType(2).subalu,
    Valid(3) && i1rs1hitStage === 3.U && FuType(3).alu && !FuType(3).subalu,
    Valid(4) && i1rs1hitStage === 4.U && FuType(4).alu && !FuType(4).subalu,
    Valid(5) && i1rs1hitStage === 5.U && FuType(5).alu && !FuType(5).subalu,
    Valid(4) && i1rs1hitStage === 4.U && FuType(4).load || Valid(5) && i1rs1hitStage === 5.U && FuType(5).load,
    Valid(4) && i1rs1hitStage === 4.U && FuType(4).mul || Valid(5) && i1rs1hitStage === 5.U && FuType(5).mul,
    Valid(6) && i1rs1hitStage === 6.U ,
    Valid(7) && i1rs1hitStage === 7.U ,
    Valid(8) && i1rs1hitStage === 8.U ,
    Valid(9) && i1rs1hitStage === 9.U
  )
  io.out1.BypassCtl.rs2bypasse0 := Seq(
    Valid(0) && i1rs2hitStage === 0.U && FuType(0).alu && !FuType(0).subalu,
    Valid(1) && i1rs2hitStage === 1.U && FuType(1).alu && !FuType(1).subalu,
    Valid(2) && i1rs2hitStage === 2.U && FuType(2).alu && !FuType(2).subalu,
    Valid(3) && i1rs2hitStage === 3.U && FuType(3).alu && !FuType(3).subalu,
    Valid(4) && i1rs2hitStage === 4.U && FuType(4).alu && !FuType(4).subalu,
    Valid(5) && i1rs2hitStage === 5.U && FuType(5).alu && !FuType(5).subalu,
    Valid(4) && i1rs2hitStage === 4.U && FuType(4).load || Valid(5) && i1rs1hitStage === 5.U && FuType(5).load,
    Valid(4) && i1rs2hitStage === 4.U && FuType(4).mul || Valid(5) && i1rs1hitStage === 5.U && FuType(5).mul,
    Valid(6) && i1rs2hitStage === 6.U ,
    Valid(7) && i1rs2hitStage === 7.U ,
    Valid(8) && i1rs2hitStage === 8.U ,
    Valid(9) && i1rs2hitStage === 9.U
  )
  io.out1.BypassCtl.rs1bypasse2 := Seq(
    Valid(4) && i1rs1hitStage === 4.U && FuType(4).alu && FuType(4).subalu,
    Valid(5) && i1rs1hitStage === 5.U && FuType(5).alu && FuType(5).subalu
  )
  io.out1.BypassCtl.rs2bypasse2 := Seq(
    Valid(4) && i1rs2hitStage === 4.U && FuType(4).alu && FuType(4).subalu,
    Valid(5) && i1rs2hitStage === 5.U && FuType(5).alu && FuType(5).subalu
  )
  io.out1.BypassCtl.rs1bypasse3 := Seq(
    io.in(1).ctrl.rfSrc1 === i1decodePkt.rd && i1decodePkt.alu && i1decodePkt.subalu && i0decodePkt.rdvalid && (i0decodePkt.mul || i0decodePkt.load),
    Valid(0) && i0rs1hitStage === 0.U && FuType(0).alu && FuType(0).subalu
      ||Valid(0) && i0rs1hitStage === 0.U && FuType(0).load
      ||Valid(0) && i0rs1hitStage === 0.U && FuType(0).mul,
    Valid(1) && i0rs1hitStage === 1.U && FuType(1).load
      || Valid(1) && i0rs1hitStage === 1.U && FuType(1).alu && FuType(1).subalu
      || Valid(1) && i0rs1hitStage === 1.U && FuType(1).mul,
    Valid(2) && i0rs1hitStage === 2.U && FuType(2).alu && FuType(2).subalu
      ||Valid(2) && i0rs1hitStage === 2.U && FuType(2).load
      ||Valid(2) && i0rs1hitStage === 2.U && FuType(2).mul,
    Valid(3) && i0rs1hitStage === 3.U && FuType(3).alu && FuType(3).subalu
      || Valid(3) && i0rs1hitStage === 3.U && FuType(3).load
      || Valid(3) && i0rs1hitStage === 3.U && FuType(3).mul
  )
  io.out1.BypassCtl.rs2bypasse3 := Seq(
    io.in(1).ctrl.rfSrc2 === i1decodePkt.rd && i1decodePkt.alu && i1decodePkt.subalu && i0decodePkt.rdvalid && (i0decodePkt.mul || i0decodePkt.load),
    Valid(0) && i0rs2hitStage === 0.U && FuType(0).alu && FuType(0).subalu
      ||Valid(0) && i0rs2hitStage === 0.U && FuType(0).load
      ||Valid(0) && i0rs2hitStage === 0.U && FuType(0).mul,
    Valid(1) && i0rs2hitStage === 1.U && FuType(1).load
      || Valid(1) && i0rs2hitStage === 1.U && FuType(1).alu && FuType(1).subalu
      || Valid(1) && i0rs2hitStage === 1.U && FuType(1).mul,
    Valid(2) && i0rs2hitStage === 2.U && FuType(2).alu && FuType(2).subalu
      ||Valid(2) && i0rs2hitStage === 2.U && FuType(2).load
      ||Valid(2) && i0rs2hitStage === 2.U && FuType(2).mul,
    Valid(3) && i0rs2hitStage === 3.U && FuType(3).alu && FuType(3).subalu
      || Valid(3) && i0rs2hitStage === 3.U && FuType(3).load
      || Valid(3) && i0rs2hitStage === 3.U && FuType(3).mul
  )
}
object DecodeIO2decodePkt {
  def apply[T <: Data](in:DecodeIO,out:decodePkt){


    out.rd := in.ctrl.rfDest
    out.rdvalid := in.ctrl.rfWen
    out.alu := in.ctrl.fuType === "b000".U
    //下面的就先置零，subalu 会在其他模块覆盖掉
    out.mul := false.B
    out.div := false.B
    out.load := false.B
    out.store := false.B
    out.subalu := false.B
  }
}
object PktPipeline {
  def apply[T<:Data](gen:T,i0In:T,i1In:T,stallFlush:StallFlushIO,out:Vec[T]) {
    val i0Pktin = Flipped(Decoupled(i0In))
    val i1Pktin = Flipped(Decoupled(i1In))
    val pipeCtl = Flipped(stallFlush)
    val Pktout = Vec(10, Decoupled(new BypassPkt))
    out := Pktout
    //stall point
    val i0pipeStall = pipeCtl.stall(0)
    val i1pipeStall = pipeCtl.stall(1)
    val memStall = pipeCtl.stall(2)
    //pipeline in, out, fire
    val pipeFire = Vec(10, Bool())

    val pipein = Vec(10, Flipped(Decoupled(new BypassPkt)))
    val pipeout = Vec(10, Decoupled(new BypassPkt))

    //pipeline0 : 1,3,5,7,9
    //pipeline1 : 0,2,4,6,8
    //pipeline in out
    pipein(0) := i0Pktin
    pipein(1) := i1Pktin
    for (i <- 2 to 9) {
      pipein(i) := pipeout(i - 2)
    }

    //fire signal
    for (i <- 0 to 9) {
      if (i == 8 || i == 9) pipeFire(i) := false.B
      else pipeFire(i) := pipeout(i).valid && pipein(i + 2).ready
    }
    //pipeline connect
    StallPointConnect(pipein(0), pipeout(0), pipeFire(0), pipeCtl.flush(0), i0pipeStall)
    StallPointConnect(pipein(1), pipeout(1), pipeFire(1), pipeCtl.flush(1), i1pipeStall)
    StallPointConnect(pipein(4), pipeout(4), pipeFire(4), pipeCtl.flush(4), memStall)
    StallPointConnect(pipein(5), pipeout(5), pipeFire(5), pipeCtl.flush(5), memStall)
    for (i <- 0 to 9) {
      if (i != 0 && i != 1 && i != 4 && i != 5) {
        PipelineConnect(pipein(i), pipeout(i), pipeFire(i), pipeCtl.flush(i))
      }
    }
    //out
    Pktout := pipeout
  }
}
class PipeCtl extends Module{
  val io = IO(new Bundle{
    val i0pipeStall = Input(Bool())
    val i1pipeStall = Input(Bool())
    val memStall = Input(Bool())
    val flush = Input(Vec(4,Bool()))  //flushPoint(0,3) -> alu0,alu1,sub_alu0,sub_alu1
    val pipeCtl = new StallFlushIO
  })

  // stall vec
  io.pipeCtl.stall(0) := io.i1pipeStall
  io.pipeCtl.stall(1) := io.i0pipeStall
  io.pipeCtl.stall(2) := io.memStall

  //flush vec
  val allStageFlushVec = Vec(10,Bool())

  //pipeline0 : 1,3,5,7,9
  //pipeline1 : 0,2,4,6,8
  val alu0FLushList = List(0,1)
  val alu1FLushList = List(0)
  val subalu0FLushList = List(0,1,2,3,4,5,6,7)
  val subalu1FLushList = List(0,1,2,3,4,5,6)

  allStageFlushVec := 0.U(10.W)asBools()
  alu0FLushList.map(i =>{
    allStageFlushVec(i) := io.flush(0)
  })

  alu1FLushList.map(i =>{
    allStageFlushVec(i) := io.flush(1)
  })

  subalu0FLushList.map(i =>{
    allStageFlushVec(i) := io.flush(2)
  })

  subalu1FLushList.map(i =>{
    allStageFlushVec(i) := io.flush(3)
  })

  val allStageFlushUInt = allStageFlushVec.asUInt()
  io.pipeCtl.flush := allStageFlushUInt.toBools()


}



